﻿using AnalyStock.DataAnalysis;
using AnalyStock.DataManage;

namespace AnalyStock.GrapImage;

public partial class ChartCandle : UserControl
{
    public bool IsMa2line { get; set; }
    public bool IsMa4line { get; set; }
    public bool IsFibonacciLine { get; set; }
    public bool IsBollLine { get; set; }
    public bool IsTrendLine { get; set; }
    public bool IsKDJ { get; set; }
    public bool IsMACD { get; set; } = true;
    public bool IsPEPB { get; set; }
    public bool IsTurnOverRate { get; set; }
    public bool IsQQRatio { get; set; }

    public void PaintLine_MainChart()
    {
        MALineCanvas.Children.Clear();
        PaintMultiTrendLine();
        PaintMultiPriceMaLine();
    }

    public void PaintLine_AttachedChart()
    {
        IndexLineCanvas.Children.Clear();
        PaintMultiIndexLine();
    }

    /// <summary>
    ///     画主图的趋势线和波浪线（拐点线）
    /// </summary>
    private void PaintMultiTrendLine()
    {
        if (IsTrendLine)
        {
            //生成K线拐点集合
            SamplePoints = currCandlePoints.CreateInflectPointsByClose(0.05f);
            PaintSingleTrendLine(Brushes.Aquamarine);
            SamplePoints.Dispose();
            //生成K线模拟模型点集合
            SamplePoints = currCandlePoints.CreateNorderFittingPointsBySample(3).FittingPoints;
            PaintSingleTrendLine(Brushes.Orange);
            //趋势描述
            new TextBlock
            {
                Text = $"{SamplePoints.AnalyTrendOfKline().TrendDescribe}",
                FontSize = 10,
                Foreground = Brushes.DarkGray,
                Opacity = 0.8
            }.SetPostion(0, 10).WithPanel(MALineCanvas);
            SamplePoints.Dispose();
        }
    }

    /// <summary>
    ///     画主图价格均线、斐波拉契线、布林线
    /// </summary>
    private void PaintMultiPriceMaLine()
    {
        MaOfKline.ForEachDo(n => n.Dispose());
        var lineID = 0;
        if (IsMa2line)
        {
            new Dictionary<int, Color>
            {
                { 5, Colors.WhiteSmoke },
                { 20, Colors.IndianRed }
            }.ForEachDo(n => MaOfKline[lineID++] = PaintMaLine(n.Key, n.Value));
        }

        if (IsMa4line)
        {
            new Dictionary<int, Color>
            {
                { 5, Colors.WhiteSmoke },
                { 10, Colors.Orange },
                { 30, Colors.IndianRed },
                { 60, Colors.DarkCyan }
            }.ForEachDo(n => MaOfKline[lineID++] = PaintMaLine(n.Key, n.Value));
        }

        if (IsFibonacciLine)
        {
            //快速移动均线带
            (MaOfKline[0], MaOfKline[1], MaOfKline[2])
                = PaintFabonacciLine((5, 8, 13), Colors.Gold);
            //慢速移动均线带
            (MaOfKline[3], MaOfKline[4], MaOfKline[5])
                = PaintFabonacciLine((55, 89, 144), Colors.DarkCyan);
        }

        if (IsBollLine)
        {
            (MaOfKline[0], MaOfKline[1], MaOfKline[2])
                = PaintBollLine(Colors.IndianRed, Colors.DarkCyan);
        }
    }

    /// <summary>
    ///     画附图的MACD、KDJ线，市盈率、市净率线、换手率线、量比
    /// </summary>
    private void PaintMultiIndexLine()
    {
        if (IsKDJ)
        {
            PaintKDJLine();
        }

        if (IsMACD)
        {
            PaintMacdLine();
        }

        if (IsPEPB)
        {
            PaintPBPELine();
        }

        if (IsTurnOverRate)
        {
            PaintTurnOverRate();
            PaintQRRatio();
        }
        //if (IsQQRatio) { PaintQRRatio(); }
    }

    /// <summary>
    ///     画交易量均线
    /// </summary>
    private void PaintMultiVolMaLine()
    {
        MaOfVol.ForEachDo(n => n.Dispose());
        //交易量的平均线
        MaOfVol[0] = PaintMaLine(5, Colors.WhiteSmoke, false);
        MaOfVol[1] = PaintMaLine(10, Colors.IndianRed, false);
    }

    /// <summary>
    ///     各类均线通用画法
    /// </summary>
    /// <param name="maCycle"></param>
    /// <param name="isPriceOrVol"></param>
    /// <returns></returns>
    private IList<MAPoint> PaintMaLine(int maCycle, Color lineColor, bool isPriceOrVol = true)
    {
        Func<CandlePoint, float> seleFunc = isPriceOrVol ? n => n.Close : n => n.Vol;
        var maSeries = currCandlePoints.Select(seleFunc)
                                       .CalculateMAVal(maCycle);
        if (maSeries.Count < 2)
        {
            return maSeries;
        }

        GetChartPoints(maSeries, isPriceOrVol)
            .PolylineWithGop(isPriceOrVol ? MALineCanvas : VolCanvas, new ShapeParam
            {
                StrokeThickness = 1.0,
                FillColor = lineColor,
                Line_Opactiy = 0.6,
                IsOnlyPolyline = true
            });
        return maSeries;
    }

    /// <summary>
    ///     根据点集合画出趋势线
    /// </summary>
    /// <param name="lineBrush"></param>
    private void PaintSingleTrendLine(Brush lineBrush)
    {
        if (SamplePoints.Count < 2)
        {
            return; //点数小于两个引起画线函数异常
        }

        var polyline = new Polyline
        {
            Stroke = lineBrush,
            StrokeThickness = 1,
            StrokeDashArray = new DoubleCollection { 4, 4 }
        };
        SamplePoints.ForEachDo(point =>
        {
            polyline.Points.Add(
                new Point(
                    GetPosition_X(point.LocationIndex),
                    GetPosition_Y(point.FittingValue, true)));
        });
        polyline.WithPanel(MALineCanvas);
    }

    /// <summary>
    ///     画Fabonacci颜色带,斐波那契移动均线
    /// </summary>
    /// <param name="EMA_Cycle"></param>
    /// <param name="lineColor"></param>
    /// <returns></returns>
    private (IList<MAPoint> Up, IList<MAPoint> Mid, IList<MAPoint> Low) PaintFabonacciLine(
        (int up, int mid, int low) EMA_Cycle, Color lineColor)
    {
        var closeArray = currCandlePoints.TakeCloseArray();
        if (!closeArray.Any())
        {
            return (null, null, null);
        }

        var emaSeries = (
            Up: closeArray.CalculateEMAVal(EMA_Cycle.up),
            Mid: closeArray.CalculateEMAVal(EMA_Cycle.mid),
            Low: closeArray.CalculateEMAVal(EMA_Cycle.low));
        var shapeParam = new ShapeParam
        {
            StrokeThickness = 0.5,
            FillColor = lineColor,
            Line_Opactiy = 0.7,
            Gop_Opacity = 0.2
        };
        //画三条线之间透明色带
        (GetChartPoints(emaSeries.Up), GetChartPoints(emaSeries.Low))
            .PolyColorTape(MALineCanvas, shapeParam);
        //画中间的移动均线
        GetChartPoints(emaSeries.Mid)
            .PolylineWithGop(MALineCanvas, shapeParam with { IsOnlyPolyline = true });
        return emaSeries;
    }

    /// <summary>
    ///     画boll线，颜色带
    /// </summary>
    /// <param name="Boll_MA_Cycle"></param>
    /// <param name="lineColor_up"></param>
    /// <param name="lineColor_low"></param>
    /// <returns></returns>
    private (IList<MAPoint> Up, IList<MAPoint> Mid, IList<MAPoint> Low) PaintBollLine(
        Color lineColor_up, Color lineColor_low)
    {
        var closeArray = currCandlePoints.TakeCloseArray();
        if (!closeArray.Any())
        {
            return (null, null, null);
        }

        var bollSeries = closeArray.CalculateBollVal();
        var shapeParam = new ShapeParam
        {
            StrokeThickness = 0.5,
            Line_Opactiy = 0.8,
            Gop_Opacity = 0.2
        };
        (GetChartPoints(bollSeries.Up), GetChartPoints(bollSeries.Mid))
            .PolyColorTape(MALineCanvas, shapeParam with { FillColor = lineColor_up });
        (GetChartPoints(bollSeries.Mid), GetChartPoints(bollSeries.Low))
            .PolyColorTape(MALineCanvas, shapeParam with { FillColor = lineColor_low });
        return bollSeries;
    }

    //转化价格或交易量数据为画图坐标点
    private PointCollection GetChartPoints(IList<MAPoint> list, bool isPriceorVol = true)
    {
        var points = new PointCollection();
        list.ForEachDo(ma =>
        {
            points.Add(new Point(
                GetPosition_X(ma.LocationIndex),
                GetPosition_Y(ma.MAValue, isPriceorVol)
            ));
        });
        return points;
    }

    /// <summary>
    ///     画MACD线
    /// </summary>
    private void PaintMacdLine()
    {
        ///获取MACD数值
        var macdSeries =
            currCandlePoints.TakeCloseArray().CalculateMACDVal();
        if (!macdSeries.Any())
        {
            return;
        }

        var (maxDif, minDif) = macdSeries.MaxMinFloat(macd => macd.DIFValue);
        var (maxMacd, minMacd) = macdSeries.MaxMinFloat(macd => macd.MACDValue);
        ///以面板中线为零轴，平均划分面板的高度，取面板Y坐标=0，对应最大值
        var maxMacdDif = new float[4]
        {
            Math.Abs(maxDif),
            Math.Abs(minDif),
            Math.Abs(maxMacd),
            Math.Abs(minMacd)
        }.Max();

        //显示刻度值 Xpos,Val
        PaintMarkQuartileLine((maxMacdDif, -maxMacdDif), "MACD", markSection: new[] { 0.5f });
        //画三条曲线
        var points = (
            Dif: new PointCollection(),
            Dea: new PointCollection(),
            Macd: new PointCollection());
        double dif_x = 0, dif_y = 0, dea_y = 0, macd_y = 0;
        macdSeries.ForEachDo(item =>
        {
            dif_x = GetPosition_X(item.LocationIndex);
            dif_y = indexPanelSize_High / 2 * (1 - (item.DIFValue / maxMacdDif));
            dea_y = indexPanelSize_High / 2 * (1 - (item.DEAValue / maxMacdDif));
            macd_y = indexPanelSize_High / 2 * (1 - (item.MACDValue / maxMacdDif));
            points.Dif.Add(new Point(dif_x, dif_y));
            points.Dea.Add(new Point(dif_x, dea_y));
            points.Macd.Add(new Point(dif_x, macd_y));
            new Line
            {
                X1 = dif_x,
                Y1 = indexPanelSize_High / 2,
                X2 = dif_x,
                Y2 = macd_y,
                Stroke = item.MACDValue > 0 ? Brushes.Red : FindResource("CandleColorGreen") as SolidColorBrush,
                StrokeThickness = 0.8
            }.WithPanel(IndexLineCanvas);
        });
        points.Dif.PolylineWithGop(IndexLineCanvas, new ShapeParam
        {
            FillColor = Colors.Orange,
            StrokeThickness = 0.8,
            IsOnlyPolyline = true
        });
        points.Dea.PolylineWithGop(IndexLineCanvas, new ShapeParam
        {
            FillColor = Colors.SkyBlue,
            StrokeThickness = 0.8,
            IsOnlyPolyline = true
        });
        points.Macd.PolylineWithGop(IndexLineCanvas, new ShapeParam
        {
            FillColor = Colors.Orange,
            StrokeThickness = 0.8,
            Gop_Opacity = 0.3,
            IsOnlyPolygop = true,
            IsXaxisOnHalfHeight = true
        });
    }

    /// <summary>
    ///     画KDJ线
    /// </summary>
    /// <param name="K_period"></param>
    private void PaintKDJLine()
    {
        var highArray = currCandlePoints.Select(p => p.High);
        var lowArray = currCandlePoints.Select(p => p.Low);
        var closeArray = currCandlePoints.Select(p => p.Close);
        var kdjSeries = CommCalculate.CalculateKDJVal(highArray, lowArray, closeArray);
        //显示刻度值 Xpos,Val
        PaintMarkQuartileLine((100, 0), "KDJ");
        if (kdjSeries.Count > 9) //RSV的取值周期是9
        {
            var points = (
                K: new PointCollection(),
                J: new PointCollection(),
                D: new PointCollection());
            double kjd_x = 0, k_y = 0, j_y = 0, d_y = 0;
            foreach (var item in kdjSeries)
            {
                kjd_x = GetPosition_X(item.LocationIndex);
                k_y = (1 - (item.KValue / 100)) * indexPanelSize_High;
                j_y = (1 - (item.JValue / 100)) * indexPanelSize_High;
                d_y = (1 - (item.DValue / 100)) * indexPanelSize_High;
                points.K.Add(new Point(kjd_x, k_y));
                points.J.Add(new Point(kjd_x, j_y));
                points.D.Add(new Point(kjd_x, d_y));
            }

            points.K.PolylineWithGop(IndexLineCanvas, new ShapeParam
            {
                FillColor = Colors.Orange,
                StrokeThickness = 0.8,
                IsOnlyPolyline = true
            });

            points.J.PolylineWithGop(IndexLineCanvas, new ShapeParam
            {
                FillColor = Colors.SkyBlue,
                StrokeThickness = 0.8,
                IsOnlyPolyline = true
            });

            points.D.PolylineWithGop(IndexLineCanvas, new ShapeParam
            {
                FillColor = Colors.SeaGreen,
                StrokeThickness = 0.8,
                Gop_Opacity = 0.5
            });
        }
    }

    /// <summary>
    ///     画PE、PB市盈率市净率曲线
    /// </summary>
    private void PaintPBPELine()
    {
        var currentFinaIndex = currKlineData.Financials.Last();
        var netAssetPS = currentFinaIndex.EquityWithoutMinority / (currKlineData.StockInfo.Total_share * 10000);
        var pbPoints = currCandlePoints.TakeCloseArray().CalculatePBVal(netAssetPS);
        if (pbPoints.Any())
        {
            var (maxPB, minPB) = pbPoints.MaxMinFloat(n => n.PB);
            var unitValSize_High = GetUnitValSize_High(maxPB, minPB);
            //画PB刻度线
            PaintMarkQuartileLine((maxPB, minPB), "PB", false);
            //画PB线,画带渐变颜色面的曲线
            var points = new PointCollection();
            pbPoints.ForEachDo(item =>
            {
                points.Add(new Point(
                    GetPosition_X(item.LocationIndex),
                    (maxPB - item.PB) * unitValSize_High));
            });
            points.PolylineWithGop(IndexLineCanvas, new ShapeParam
            {
                FillColor = Colors.Orange,
                StrokeThickness = 0.8
            });
        }
        var dynamicEPS = currentFinaIndex.ReportDt[4..6] switch
        {
            "03" => currentFinaIndex.EPS * 4,
            "06" => currentFinaIndex.EPS * 2,
            "09" => currentFinaIndex.EPS * 4 / 3,
            _ => currentFinaIndex.EPS
        };
        var pePoints = currCandlePoints.TakeCloseArray().CalculatePEVal(dynamicEPS);
        if (!pePoints.Any())
        {
            return;
        }
        //显示PE刻度值
        var (maxPE, minPE) = pePoints.MaxMinFloat(n => n.PE);
        PaintMarkQuartileLine((maxPE, minPE), "DynamicPE");
    }

    /// <summary>
    ///     画换手率曲线
    /// </summary>
    private void PaintTurnOverRate()
    {
        var turnOverRates =
            currCandlePoints.Select(n => n.Vol)
                .CalculateTurnOverRate(currKlineData.StockInfo.Float_share);
        if (!turnOverRates.Any())
        {
            return;
        }
        //画中间刻度线
        var (maxRate, minRate) = turnOverRates.MaxMinFloat(n => n.TurnOverRateVal);
        var unitValSize_High = GetUnitValSize_High(maxRate, minRate);
        PaintMarkQuartileLine((maxRate, minRate), "TurnOverRate");
        //画带渐变颜色面的曲线
        PointCollection points = new(turnOverRates.Count);
        turnOverRates.ForEachDo(item =>
        {
            points.Add(new Point(
                GetPosition_X(item.LocationIndex),
                (maxRate - item.TurnOverRateVal) * unitValSize_High));
        });
        points.PolylineWithGop(IndexLineCanvas, new ShapeParam
        {
            FillColor = Colors.IndianRed,
            StrokeThickness = 0.8
        });
    }

    private void PaintQRRatio()
    {
        var qRRatios = currCandlePoints.Select(n => n.Vol)
            .CalculateQRRatio(currKlineData.MinuteDatas?.Count ?? 0);
        if (!qRRatios.Any())
        {
            return;
        }

        //画中间刻度线
        var (maxRate, minRate) = qRRatios.MaxMinFloat(n => n.QRRatioVal);
        var unitValSize_High = GetUnitValSize_High(maxRate, minRate);
        PaintMarkQuartileLine((maxRate, minRate), "QRR", false);
        //画带渐变颜色面的曲线
        PointCollection points = new(qRRatios.Count);
        qRRatios.ForEachDo(item =>
        {
            points.Add(new Point(
                GetPosition_X(item.LocationIndex),
                (maxRate - item.QRRatioVal) * unitValSize_High));
        });
        points.PolylineWithGop(IndexLineCanvas, new ShapeParam
        {
            FillColor = Colors.SkyBlue,
            StrokeThickness = 0.8,
            Line_Opactiy = 0.8,
            IsOnlyPolyline = true
        });
    }

    private double GetUnitValSize_High(float maxVal, float minVal)
    {
        return maxVal != minVal
            ? indexPanelSize_High / (maxVal - minVal)
            : indexPanelSize_High / maxVal;
    }

    /// <summary>
    ///     通用指标刻度线，根据百分比,如果有刻度线，则刻度值在左边显示对应为主指标
    /// </summary>
    /// <param name="markSection"></param>
    private void PaintMarkQuartileLine((float maxVal, float minVal) indexValRange,
                                       string indexName,
                                       bool isMainIndex = true,
                                       float[] markSection = default)
    {
        markSection ??= new float[] { 0.0f, 0.25f, 0.5f, 0.75f, 1.0f };
        //画刻度直线，主指标才画刻度线
        if (isMainIndex)
        {
            markSection.ForEachDo(val =>
            {
                new Line
                {
                    X1 = candlePanelSize.Left,
                    Y1 = indexPanelSize_High * val,
                    X2 = candlePanelSize.Left + candlePanelSize.Width,
                    Y2 = indexPanelSize_High * val,
                    Stroke = Brushes.Gray,
                    StrokeThickness = 0.3
                }.WithPanel(IndexLineCanvas);
            });
        }

        //写每个线的刻度值
        markSection.ForEachDo(val =>
        {
            new TextBlock
            {
                Text = $"{(indexValRange.maxVal * (1.0f - val)) + (indexValRange.minVal * val):F1}",
                FontSize = 9,
                Foreground = Brushes.DarkCyan
            }.SetPostion((val * indexPanelSize_High) - 6.0,
                isMainIndex ? 2.0f : ActualWidth - 15.0f).WithPanel(IndexLineCanvas);
        });

        //写指标名称
        new TextBlock
        {
            Text = $"<{indexName}>",
            FontSize = 9,
            Foreground = Brushes.DarkCyan
        }.SetPostion(
            indexPanelSize_High,
            isMainIndex ? 25.0f : ActualWidth - 50.0f).WithPanel(IndexLineCanvas);
    }
}